package relu.fbs;
import java.util.*;

/** The \c DataValue class holds data corresponding to a conceptual datum.  
 * It permits averaging or summation of several lower level data fields */
public class DataValue {
	private final List<Double> values = new ArrayList<Double>();
	private final List<Double> weights = new ArrayList<Double>();
	private double average=-1;
	private double sum=-1;
	/** True is the average has not yet been calculated */
	private boolean avNeedsCalc=true;
	/** True is the sum has not yet been calculated */
	private boolean sumNeedsCalc=true;
	/** True if the present data value should be averaged. False if it should be a sum */
	private boolean isAverageable=false;
	
	/** Create a DataValue by specifying whether to average or not */
	public DataValue(boolean isAv){
		isAverageable=isAv;
	}
	
	/** Create a non-averageable value by specifying its initial value */
	public DataValue(double v){
		add(v);
	}
	/** Add a weighted value to the average.*/
	public void add(double v,double w){
		if ( isAverageable ){
			values.add(v);
			weights.add(w);
			avNeedsCalc=true;
			sumNeedsCalc=true;
		} else {
			throw new Error("Can't set weight for non-averageable value");
		}
	}
	
	/** Add a value without knowing its weight or whether it is averageable or not ... */
	public void add(double v){
		if ( isAverageable){
			if ( v == 0 ){
				add(v,0.0);
			} else {
				add(v,1.0);
			}
		} else {
			values.add(v);
			sumNeedsCalc=true;
		}
	}

	public void calculateAv(){
		double total=0;
		double norm=0;
		for ( int i=0;i<values.size();i++){
			double wt=weights.get(i);
			total+=values.get(i)*wt;
			norm+=wt;
		}
		if ( norm!=0 ){
			average=total/norm;
		} else {
			average=0;
		}
		avNeedsCalc=false;
	}
	private void calculateSum(){
		sum=0;
		for(Double v:values){
			sum+=v;
		}
		sumNeedsCalc=false;
	}
	
	/** Return the average of an averagable object */
	public double avValue(){
		if ( !isAverageable){
			throw new Error("Attempt to get average for non-averable value");
		}
		if (avNeedsCalc){
			calculateAv();
		}
		return average;
	}
	
	/** Return the sum of a non-averageable object */
	public double sumValue(){
		if ( sumNeedsCalc){
			calculateSum();			
		}
		return sum;
	}
	
	/** Automatically determine whether to return an average or a sum and return */
	public double autoValue(){
		if ( isAverageable ){
			return avValue();
		} else {
			return sumValue();
		}
	}
	
}